<script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref } from 'vue'
import * as monaco from 'monaco-editor'

import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'

// 导入 JSON5 解析库，用于语言服务（格式化等）
import JSON5 from 'json5'

const props = defineProps({
  modelValue: String,
  readOnly: {
    type: Boolean,
    default: false,
  },
  language: {
    type: String,
    default: 'json5',
  },
  parentRef: {
    type: Object,
    default: null,
  },
})

const emit = defineEmits(['update:modelValue']);

// eslint-disable-next-line no-restricted-globals
(self as any).MonacoEnvironment = {
  getWorker(workerId: any, label: any) {
  // 同时支持json和json5
    if (label === 'json' || label === 'json5') {
      return new JsonWorker()
    }
    return new EditorWorker()
  },
}

const editorRef = ref()
let editorInstance: any = null

// 定义 JSON5 的语法高亮规则和配置
function registerJson5Language() {
  monaco.languages.register({ id: 'json5' })

  monaco.languages.setMonarchTokensProvider('json5', {
    tokenizer: {
      root: [
        { include: 'common' },
      ],
      common: [
        // 空格
        { regex: /\s+/, action: { token: 'white' } },

        // 注释
        { regex: /\/\/.*$/, action: { token: 'comment' } },
        { regex: /\/\*[\s\S]*?\*\//, action: { token: 'comment' } },

        // 字符串
        { regex: /"([^"\\]|\\.)*"/, action: { token: 'string' } },
        { regex: /'([^'\\]|\\.)*'/, action: { token: 'string' } },

        // 数字
        { regex: /-?0x[\da-fA-F_]+/, action: { token: 'number.hex' } },
        { regex: /-?\d[\d_]*(?:\.\d[\d_]*)?(?:e[+-]?\d[\d_]*)?/i, action: { token: 'number' } },
        { regex: /(Infinity|-Infinity|NaN)\b/, action: { token: 'number' } },

        // 关键字
        { regex: /(true|false|null)\b/, action: { token: 'keyword' } },

        // 无引号的键
        { regex: /[a-z_$][\w$]*/i, action: { token: 'key' } },

        // 标点符号
        { regex: /[{}[\]]/, action: { token: '@brackets' } },
        { regex: /:/, action: { token: 'delimiter.colon' } },
        { regex: /,/, action: { token: 'delimiter.comma' } },
      ],
    },
  })

  monaco.languages.setLanguageConfiguration('json5', {
    comments: {
      lineComment: '//',
      blockComment: ['/*', '*/'],
    },
    brackets: [
      ['{', '}'],
      ['[', ']'],
    ],
    autoClosingPairs: [
      { open: '{', close: '}' },
      { open: '[', close: ']' },
      { open: '"', close: '"' },
      { open: '\'', close: '\'' },
    ],
    surroundingPairs: [
      { open: '{', close: '}' },
      { open: '[', close: ']' },
      { open: '"', close: '"' },
      { open: '\'', close: '\'' },
    ],
  })

  // 可选：添加格式化支持
  monaco.languages.registerDocumentFormattingEditProvider('json5', {
    provideDocumentFormattingEdits(model, options) {
      const text = model.getValue()
      try {
        const obj = JSON5.parse(text)
        const formattedText = JSON5.stringify(obj, null, options.tabSize)
        return [{
          range: model.getFullModelRange(),
          text: formattedText,
        }]
      }
      catch (e) {
        console.error('JSON5 格式化失败:', e)
        return []
      }
    },
  })
}

// 自定义 JSON5 验证器
function createJson5Validator() {
  return {
    provideDiagnostics(model: any) {
      const markers: any[] = []
      try {
        JSON5.parse(model.getValue())
      }
      catch (error: any) {
        // 直接从 error 对象中获取行号和列号，而不是解析错误消息
        // JSON5 的错误对象通常包含 lineNumber 和 columnNumber
        if (error.lineNumber && error.columnNumber) {
          markers.push({
            severity: monaco.MarkerSeverity.Error,
            message: error.message,
            startLineNumber: error.lineNumber,
            startColumn: error.columnNumber,
            endLineNumber: error.lineNumber,
            endColumn: error.columnNumber + 1, // 标记一个字符的长度
          })
        }
        else {
          // 如果获取不到行号和列号，回退到通用错误处理
          markers.push({
            severity: monaco.MarkerSeverity.Error,
            message: error.message,
            startLineNumber: 1,
            startColumn: 1,
            endLineNumber: 1,
            endColumn: 10,
          })
        }
      }
      monaco.editor.setModelMarkers(model, 'json5', markers)
    },
  }
}

const wheelOptions = { capture: true, passive: false }
function customWheel(event: any) {
  if (!editorInstance.getDomNode().contains(event.target))
    return

  const deltaY = event.deltaY
  const scrollTop = editorInstance.getScrollTop()
  const editorHeight = editorInstance.getLayoutInfo().height
  const scrollHeight = editorInstance.getScrollHeight()

  // 判断是否滚动到底部
  const isAtBottom = scrollTop + editorHeight >= scrollHeight

  // 判断是否滚动到顶部
  const isAtTop = scrollTop <= 0

  if ((isAtTop && deltaY < 0) || (isAtBottom && deltaY > 0)) {
    event.stopPropagation() // 允许父容器滚动
  }
}

onMounted(() => {
  // 在创建编辑器前注册 JSON5 语言
  registerJson5Language()

  editorInstance = monaco.editor.create(editorRef.value, {
    tabSize: 2,
    automaticLayout: true,
    scrollBeyondLastLine: false,
    value: props.modelValue, // 编辑器初始显示文字
    language: props.language, // 此处使用的python，其他语言支持自行查阅demo
    theme: 'vs', // 官方自带三种主题vs, hc-black, or vs-dark
    selectOnLineNumbers: true, // 显示行号
    roundedSelection: false,
    readOnly: props.readOnly, // 只读
    cursorStyle: 'line', // 光标样式
    glyphMargin: true, // 字形边缘
    useTabStops: false,
    fontSize: 14, // 字体大小
    autoIndent: 'advanced', // 自动布局
    quickSuggestionsDelay: 100, // 代码提示延时
    lineNumbersMinChars: 1,
    minimap: {
      enabled: false, // 关闭小地图,
    },
  })

  // 为 JSON5 设置自定义验证器
  if (props.language === 'json5') {
    const validator = createJson5Validator()
    const model = editorInstance.getModel()
    validator.provideDiagnostics(model)

    // 监听内容变化进行验证
    model.onDidChangeContent(() => {
      validator.provideDiagnostics(model)
    })
  }

  editorInstance.onDidChangeModelContent(() => {
    const value = editorInstance.getValue()
    emit('update:modelValue', value)
  })

  // 滚动事件处理
  if (props.parentRef) {
    props.parentRef.addEventListener('wheel', customWheel, wheelOptions)
  }
})

onBeforeUnmount(() => {
  if (editorInstance) {
    editorInstance.dispose() // 清理编辑器实例和监听器
    editorInstance = null
  }

  if (props.parentRef) {
    props.parentRef.removeEventListener('wheel', customWheel, wheelOptions)
  }
})
</script>

<template>
  <div ref="editorRef" class="editor" />
</template>

<style scoped>
.editor {
  height: 100%;
}

/* 去除光标所在行边框 */
.editor :deep() .view-overlays .current-line {
  border: none;
}

/* 去除光标所在行边框 */
.editor :deep() .view-overlays.focused .current-line {
  background-color: #f9f9f9;
}
</style>
